package test;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import com.algorithm.basic.BasicMoead;
import com.problem.tsp.TSPProblem;
import com.util.bean.METHOD;
import com.util.set.impl.KDTreeSet;
import com.util.set.inter.ExSetInter;
import com.util.solution.impl.TSPSolution;

public class ExHMOEAD extends BasicMoead{
	private TSPProblem problem;
	private int run_num;
	private int niche = 50;
	private int method;
	
	public ExSetInter<TSPSolution> exSet = new KDTreeSet<TSPSolution>(2);
	
	public ExSetInter<TSPSolution> exStartSolutions = new KDTreeSet<TSPSolution>(2);
	
	public ExHMOEAD(TSPProblem problem, int type, int run_num, int method){
		super(problem.obj_num, type);
		this.problem = problem;
		this.run_num = run_num;
		this.method = method;
		weights = new double[problem.sub_num*problem.obj_num];
		nicheNeighbors = new int[problem.sub_num][niche];
		boolean ret = initialWeights(problem.sub_num, niche);
		if(!ret) System.exit(0);
		this.bind(this.problem, this.method);
	}

	public void initialIdealPoint(){
		for(int i=0;i<problem.sub_num;i++){
			int [] values = problem.solutionArray[i].object_val;
			this.udpateIdealPoint(values);
		}
	}
	
	public void execute(int iteration){
		long startMili=System.currentTimeMillis();
		ArrayList<TSPSolution> startSolutions = new ArrayList<TSPSolution>();
		for(int i = 0;i<iteration;i++){
			System.out.println("iteration of : "+(i+1));
			this.executeMoead();
			startSolutions.clear();
			for(TSPSolution s : problem.solutionArray){
				if(!s.isSearched())
					try {
						if(this.exSet.add(s.clone())){
							startSolutions.add(s.clone());
						}
					} catch (CloneNotSupportedException e) {
						e.printStackTrace();
					}
			}
			this.executePLS(startSolutions);
		}
		long endMili=System.currentTimeMillis();
		System.out.println((endMili-startMili)/1000+" s ");
		String fileName;
		if(this.method == METHOD.WS){
			fileName = "files/results/moead/test/"+problem.problem_des+problem.city_num+"/"+problem.problem_des+problem.city_num+"_ws_"+run_num+"_ex.txt";
		}else if(this.method == METHOD.TCH){
			fileName = "files/results/moead/test/"+problem.problem_des+problem.city_num+"/"+problem.problem_des+problem.city_num+"_tch_"+run_num+"_ex.txt";
		}else{
			fileName = "files/results/moead/test/"+problem.problem_des+problem.city_num+"/"+problem.problem_des+problem.city_num+"_pbi_"+run_num+"_ex.txt";
		}
		exSet.saveObjectiveResults(fileName);
	}
	public void executePLS(List<TSPSolution> list){
		for(TSPSolution s : list){
			this.generateNeighborhood2(s);
		}
	}
	public void executeMoead(){
		int count = 0;
		for(int i=0;i<problem.sub_num;i++){
			TSPSolution curSolution = problem.solutionArray[i];
			if(curSolution.isSearched()==false){
				count++;
				problem.solutionArray[i].setSearched(true);
				try {
					TSPSolution s = curSolution.clone();
					this.generateNeighborhood(s);
				} catch (CloneNotSupportedException e) {
					e.printStackTrace();
				}
			}
		}
		if(count==0) {
			//ArrayList<TSPSolution> startSolutions = new ArrayList<TSPSolution>();
			//for(int i=0;i<problem.sub_num;i++) startSolutions.add(problem.solutionArray[i]);
			//this.executePLS(startSolutions);
			this.resetRefLines();
		}
	}
	/**
	 *  reset the reference lines
	 */
	public void resetRefLines(){
		System.out.println("reset weight vectors...");
		Random rand = new Random();
		double u = 20;
		for(int i=0;i<this.problem.sub_num;i++){
			int axis = rand.nextInt(this.problem.obj_num);
			double x = rand.nextDouble();
			if(x<=0.5){
				this.weights[i*this.problem.obj_num+axis] *= this.sigmod(x, u);
			}else{
				this.weights[i*this.problem.obj_num+axis] *= 1+this.sigmod(x, u);
			}
			//normalization
			double sum = 0;
			for(int j=0;j<this.problem.obj_num;j++){
				sum += this.weights[i*this.problem.obj_num+j]; 
			}
			for(int j=0;j<this.problem.obj_num;j++){
				this.weights[i*this.problem.obj_num+j] /= sum;
			}
		}
		this.bind(problem, method);
		for(TSPSolution s : this.problem.solutionArray){
			s.setSearched(false);
		}
	}
	public double sigmod(double x, double u){
		double y = 1/(1+Math.pow(Math.E, u*(0.5-x)));
		return y;
	}
	
	/**
	 * generate the neighborhood of s
	 * @param s
	 */
	public void generateNeighborhood(TSPSolution s){
		int [] rounte = new int[problem.city_num+1];
		for(int i=0;i<problem.city_num;i++) rounte[i] = s.route[i];
		rounte[problem.city_num] = rounte[0];
		int [] pos = new int[problem.city_num];
		problem.calculatePosition(rounte, pos);
		int pos1 = 0;
		int pos2 = 0;
		for(int k=0;k<problem.city_num;k++){
			int i = k;
			pos1 = i;
			int curIndex = rounte[i];
			int nextIndex = rounte[i+1];
			ArrayList<Integer> candidate = problem.candidateList.get(curIndex);
			Iterator<Integer> iter = candidate.iterator();
			while(iter.hasNext()){
				int next = iter.next();
				pos2 = pos[next];
				int curIndex1 = rounte[pos2];
				int nextIndex1 = rounte[pos2+1];
				if(curIndex == nextIndex1) continue;
				if(curIndex == curIndex1) continue;
				if(nextIndex == nextIndex1) continue;
				if(curIndex1 == nextIndex) continue;
				int betterTimes = 0;
				TSPSolution solution = new TSPSolution(problem.city_num, problem.obj_num, -1);
				for(int j=0;j<problem.obj_num;j++){
					int gain = problem.disMatrix[j*problem.city_num*problem.city_num+curIndex*problem.city_num+curIndex1] +
							problem.disMatrix[j*problem.city_num*problem.city_num+nextIndex*problem.city_num+nextIndex1] - 
							(problem.disMatrix[j*problem.city_num*problem.city_num+curIndex*problem.city_num+nextIndex]+
							problem.disMatrix[j*problem.city_num*problem.city_num+curIndex1*problem.city_num+nextIndex1]);
					if(gain<0) betterTimes++;
					solution.object_val[j] = s.object_val[j] + gain;
				}
				if(betterTimes==0) continue;
				solution.route = s.route.clone();
				solution.index = s.index;
				problem.converse(pos1, pos2, solution.route, 0);
				this.udpateSubSolutions(solution);
			}
		}
	}
	
	/**
	 * generate the neighborhood of s
	 * @param s
	 */
	public void generateNeighborhood2(TSPSolution s){
		int [] rounte = new int[problem.city_num+1];
		for(int i=0;i<problem.city_num;i++) rounte[i] = s.route[i];
		rounte[problem.city_num] = rounte[0];
		int [] pos = new int[problem.city_num];
		problem.calculatePosition(rounte, pos);
		int pos1 = 0;
		int pos2 = 0;
		for(int k=0;k<problem.city_num;k++){
			int i = k;
			pos1 = i;
			int curIndex = rounte[i];
			int nextIndex = rounte[i+1];
			ArrayList<Integer> candidate = problem.candidateList.get(curIndex);
			Iterator<Integer> iter = candidate.iterator();
			while(iter.hasNext()){
				int next = iter.next();
				pos2 = pos[next];
				int curIndex1 = rounte[pos2];
				int nextIndex1 = rounte[pos2+1];
				if(curIndex == nextIndex1) continue;
				if(curIndex == curIndex1) continue;
				if(nextIndex == nextIndex1) continue;
				if(curIndex1 == nextIndex) continue;
				int betterTimes = 0;
				TSPSolution solution = new TSPSolution(problem.city_num, problem.obj_num, -1);
				for(int j=0;j<problem.obj_num;j++){
					int gain = problem.disMatrix[j*problem.city_num*problem.city_num+curIndex*problem.city_num+curIndex1] +
							problem.disMatrix[j*problem.city_num*problem.city_num+nextIndex*problem.city_num+nextIndex1] - 
							(problem.disMatrix[j*problem.city_num*problem.city_num+curIndex*problem.city_num+nextIndex]+
							problem.disMatrix[j*problem.city_num*problem.city_num+curIndex1*problem.city_num+nextIndex1]);
					if(gain<0) betterTimes++;
					solution.object_val[j] = s.object_val[j] + gain;
				}
				if(betterTimes==0) continue;
				solution.route = s.route.clone();
				solution.index = s.index;
				
				if(this.exSet.add(solution)){
					problem.converse(pos1, pos2, solution.route, 0);
//					try {
//						//exStartSolutions.add(solution.clone());
//					} catch (CloneNotSupportedException e) {
//						e.printStackTrace();
//					}
				}
			}
		}
	}
	
	public boolean udpateSubSolutions(TSPSolution s){
		this.udpateIdealPoint(s.object_val);
		boolean ret = false;
		for(int i=0;i<niche;i++){
			TSPSolution curSolution = problem.solutionArray[this.nicheNeighbors[s.index][i]];
			double fitness = 0;
			if(this.method == METHOD.WS){
				fitness = getWsValue(curSolution.index, s.object_val);
			}else if(this.method == METHOD.TCH){
				curSolution.fitness = getTchValue(curSolution.index, curSolution.object_val);
				fitness = getTchValue(curSolution.index, s.object_val);
			}else{
				curSolution.fitness = getPbiValue(curSolution.index, curSolution.object_val);
				fitness = getPbiValue(curSolution.index, s.object_val);
			}
			if(curSolution.fitness>fitness){
				curSolution.fitness = fitness;
				curSolution.object_val = s.object_val.clone();
				curSolution.route = s.route.clone();
				curSolution.setSearched(false);
				ret = true;
				//return ret;
			}
		}
		return ret;
	}
	
	public static void main(String[]args){
		TSPProblem tsp = new TSPProblem();
		ExHMOEAD moead = new ExHMOEAD(tsp, -1, 0, 0);
		moead.execute(800);		
	}
}
